package districting;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.awt.Color;
import java.util.*;
import com.vividsolutions.jts.geom.*;

import de.micromata.opengis.kml.v_2_2_0.Document;
import de.micromata.opengis.kml.v_2_2_0.MultiGeometry;

public class Division extends Zone implements Colorable {

   private String name;

   private int id;

   private Map<Candidate, Integer> resultsPrimary;

   private int totalVotesPrimary;
   
   private DivisionMethod divMethod;

   public Division(String name, int id, DivisionMethod divMethod) {
      this.name = name;
      this.id = id;
      this.booths = new HashSet<PollingBooth>();
      this.resultsPrimary = new HashMap<Candidate, Integer>();
      this.totalVotes2PP = 0;
      this.totalVotesPrimary = 0;
      this.votingPopulation = 0;
      this.polygon = new GeometryFactory().createMultiPolygon(null);
      this.Labor2PPVotes = 0;
      this.Coalition2PPVotes = 0;
      Random rand = new Random(id);
      this.color = new Color(rand.nextInt());
      this.pbGeometry = new GeometryFactory().createMultiPoint(new Coordinate[] {});
      this.divMethod = divMethod;
   }
   
   public void addPB(PollingBooth pb){
      super.addPB(pb);
      pb.setDivision(this, this.divMethod);
   }
   
   public void removePB(PollingBooth pb){
      super.removePB(pb);
      pb.setDivision(null, this.divMethod);
   }

   public int getId() {
      return id;
   }

   /**
    * @return the name
    */
   public String getName() {
      return name;
   }

   /**
    * @param name
    *           the name to set
    */
   public void setName(String name) {
      this.name = name;
   }

   public void addCandidatePrimary(Candidate c) {
      resultsPrimary.put(c, 0);
   }

   public Candidate getCandidatePrimary(int id) {
      for (Candidate c : resultsPrimary.keySet()) {
         if (c.getId() == id) {
            return c;
         }
      }
      throw new NullPointerException();
   }

   public void collatePrimaryResults() {
      for (PollingBooth pb : booths) {
         for (Candidate c : pb.getResultsPrimary().keySet()) {
            int numVotes = resultsPrimary.get(c) + pb.getPrimaryVotes(c);
            totalVotesPrimary += pb.getPrimaryVotes(c);
            resultsPrimary.put(c, numVotes);
         }
      }
   }

   public void collate2PP() {
      Labor2PPVotes = 0;
      Coalition2PPVotes = 0;
      totalVotes2PP = 0;
      for (PollingBooth pb : booths) {
         Labor2PPVotes += pb.getLabor2PPVotes();
         Coalition2PPVotes += pb.getCoalition2PPVotes();
         totalVotes2PP += pb.getTotalVotes();
      }
   }

   public void print2PPResult() {
      System.out.println(name + " Labor: " + Labor2PPVotes + " Coalition: "
            + Coalition2PPVotes + " Total: " + totalVotes2PP);
   }
   
   //Horrible hack - fix it
   public void setPolygon() {
      for (PollingBooth pb : booths) {
         if (pb.getPolygon() != null) {
            polygon = polygon.union(pb.getPolygon());
         }
      }
   }
   
   public Color getColor(){
      return color;
   }
   
   public de.micromata.opengis.kml.v_2_2_0.Polygon makePolygon(Polygon geom) {
      de.micromata.opengis.kml.v_2_2_0.Polygon new_polygon = new de.micromata.opengis.kml.v_2_2_0.Polygon();
      de.micromata.opengis.kml.v_2_2_0.LinearRing boundary = new de.micromata.opengis.kml.v_2_2_0.LinearRing();
      
      for (Coordinate coord : geom.getExteriorRing().getCoordinates()) {
         double x = coord.x;
         double y = coord.y;
         boundary.addToCoordinates(x, y);
      }
      new_polygon.createAndSetOuterBoundaryIs().setLinearRing(boundary);
      
      for (int i = 0 ; i < geom.getNumInteriorRing() ; i++) {
         de.micromata.opengis.kml.v_2_2_0.LinearRing innerRing = new de.micromata.opengis.kml.v_2_2_0.LinearRing();
         for (Coordinate coord : geom.getInteriorRingN(i).getCoordinates()) {
            double x = coord.x;
            double y = coord.y;
            innerRing.addToCoordinates(x, y);
         }
         new_polygon.createAndAddInnerBoundaryIs().setLinearRing(innerRing);
      }
      return new_polygon;
   }
   
   public void createAndSetPlacemarks(Document document, Geometry geom) {
      String description;
      Geometry maxPolygon = null;
      double maxArea = 0;
      for (int i = 0 ; i < geom.getNumGeometries() ; i++) {
         if (geom.getGeometryN(i).getArea() > maxArea) {
            maxArea = geom.getGeometryN(i).getArea();
            maxPolygon = geom.getGeometryN(i);
         }
      }

      Coordinate coordinate = maxPolygon.getCentroid().getCoordinate();
      description =
         name + "\n" +
         "Total Votes = " + totalVotes2PP + "\n" +
         "Labor = " + (Labor2PPVotes / (float) totalVotes2PP * 100) + "%\n" +
         "Coalition = " + (Coalition2PPVotes / (float) totalVotes2PP * 100) + "%";
      document.createAndAddPlacemark().withStyleUrl("#balloonStyle").withDescription(description)
      .createAndSetPoint().addToCoordinates(coordinate.x, coordinate.y);
      
      /*for (int i = 0 ; i < geom.getNumGeometries() ; i++) {
         if (geom.getGeometryN(i) != maxPolygon) {
            coordinate = geom.getGeometryN(i).getCentroid().getCoordinate();
            description = name;
            document.createAndAddPlacemark().withStyleUrl("#balloonStyle").withDescription(description)
            .createAndSetPoint().addToCoordinates(coordinate.x, coordinate.y);
         }
      }*/
   }
   
   public void toKml(Document document) {
      Geometry geom = getArea();
      if (geom != null) {
         document.createAndAddStyle().withId(getHTMLColorString(color)).createAndSetPolyStyle().withColor("7f"+getHTMLColorString(color));
         createAndSetPlacemarks(document, geom);
         MultiGeometry multigeometry = document.createAndAddPlacemark().withStyleUrl("#"+getHTMLColorString(color)).createAndSetMultiGeometry();
         for (int i = 0; i < geom.getNumGeometries() ; i++) {
            multigeometry.addToGeometry(makePolygon((Polygon) geom.getGeometryN(i)));
         }
      }
   }

   /* (non-Javadoc)
    * @see java.lang.Object#hashCode()
    */
   @Override
   public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result
            + ((divMethod == null) ? 0 : divMethod.hashCode());
      result = prime * result + id;
      return result;
   }

   /* (non-Javadoc)
    * @see java.lang.Object#equals(java.lang.Object)
    */
   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      if (obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
      Division other = (Division) obj;
      if (divMethod == null) {
         if (other.divMethod != null)
            return false;
      } else if (!divMethod.equals(other.divMethod))
         return false;
      if (id != other.id)
         return false;
      return true;
   }
   
   
   

}